feat(spa): audit fixes (widget kinds, iframe sandbox, checks, parity, i18n, perf) + dep ^1.6.0 + 1.11.0#675
Merged
Conversation
…8n (#664, #669) #664: adaptFormSpec now maps all 23 WidgetKind values onto an exhaustive Record<WidgetKind, WidgetHint|undefined> (a new kind is a compile error). FieldInput renders hidden as a real hidden input, split-datetime as date+time, select-date as a date input, checkbox-multiple/select-multiple as a checkbox bank / <select multiple>, autocomplete(-multiple), and file (limited control + legacy-admin note, upload tracked by #241). Kinds with no faithful control map to an explicit operator-visible unsupported_widget tracked fallback — never a silent wrong control. The test asserts every enum member maps sensibly. #669: FieldInput's Lookup ↗ / lookup aria-label, — select — / (none), and the time/array/range/FK placeholders now go through t(); new keys added to the es/fr/pt catalogs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
legacy_url from the form-spec legacy-iframe fallback is now validated before reaching the <iframe src> / <a href> sinks: only a same-origin http(s) URL is framed/linked (new safeLegacyUrl helper, mirroring action-redirect.ts); a javascript:/data:/blob: scheme or off-origin target renders an inert error card. The iframe carries sandbox="allow-forms allow-scripts allow-same-origin". SECURITY.md §QSEC-03 adds frame-src 'self' and documents the X-Frame-Options ↔ legacy-iframe interaction. Tests cover the validator and the rejection paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The changelist wire emits list_display_links; the Table/RecordCardList primitives now link exactly the columns flagged isLink (set by ListPage from the wire) instead of hard-pinning the first column — and link none / make the row inert when the admin sets list_display_links = None. A pre-1.6.0 backend (field absent) keeps the legacy first-column behaviour. Table tests cover the explicit-link, no-link, and legacy-fallback paths. Note: ListPage also gains virtualizeRows on the Table for #670 (committed next). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New django_admin_react/checks.py registers a manage.py check validator with actionable hints: django_admin_rest_api missing from INSTALLED_APPS (Error), unimportable ADMIN_SITE dotted path (Error), unknown DJANGO_ADMIN_REACT keys (Error, at startup vs a lazy ValueError), API_URL_PREFIX requiring the consumer to mount the API (Warning), and a missing built bundle/Vite manifest (Warning). Registered in AppConfig.ready(). Adds django_admin_rest_api to the test project's INSTALLED_APPS (per its design) and a full-coverage test_checks.py. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… parity fixes (#670, #668) #670: LoginPage and CreatePage are now React.lazy-loaded at the route boundary (out of the first-paint main chunk), wrapped in Suspense; App.tsx's "Page not found." now goes through t(). The "Show all N" (?all) list path uses native content-visibility row windowing (Table.virtualizeRows, wired in ListPage). #668: README parity table corrected — raw_id_fields / radio_fields / filter_horizontal flip to ✅ (they ship today), stale "does NOT carry through" entries removed, and a new section documents empty_value_display (hard-coded —), custom each_context, and list_select_related. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closed
6 tasks
This was referenced Jun 2, 2026
[security] Validate legacy_url and sandbox the legacy iframe (#659) before using it as src/href
#665
Closed
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the v1.9.0 security + UX/architecture audit findings (#664–#670) and raises the
django-admin-rest-apifloor to^1.6.0. Version bumped 1.10.1 → 1.11.0.Per-issue summary
widget.kindrenders faithfully.adaptFormSpecnow maps all 23WidgetKindvalues onto an exhaustiveRecord<WidgetKind, WidgetHint | undefined>(a new kind is a compile error).FieldInputgained branches:hidden→ real hidden input (was a visible editable field),split-datetime→ date + time,select-date→ date input,checkbox-multiple/select-multiple→ checkbox bank /<select multiple>,autocomplete/autocomplete-multiple, andfile→ limited control + legacy-admin note (upload still Form: FileField / ImageField upload (multipart) — currently read-only #241). Kinds with no faithful control map to an explicit, operator-visibleunsupported_widgettracked fallback.adaptFormSpec.test.tsasserts every enum member maps sensibly.prepopulated_fieldsis already consumed for the add form (Form: prepopulated_fields — auto-fill (e.g. slug) from other fields while typing #245/[audit] prepopulated_fields slug-from-name auto-fill — verify it fires on the SPA create form #629); the form-spec add path remains a follow-up.safeLegacyUrlaccepts only same-originhttp(s);javascript:/data:/blob:/off-origin render an inert error card. Iframe getssandbox="allow-forms allow-scripts allow-same-origin". SECURITY.md §QSEC-03 addsframe-src 'self'+ documents the X-Frame-Options interaction. Tested.list_display_linkshonoured.Table/RecordCardListlink exactly the configured columns (via a newisLinkflag set from the wire), none whenlist_display_links = None(rows inert); pre-1.6.0 backend keeps the legacy first-column fallback. Tested.django.core.checks. Newchecks.pyvalidates rest-api installed,ADMIN_SITEimports, unknown settings keys,API_URL_PREFIXcoherence, and the built bundle/manifest — Errors/Warnings with hints. Full-coverage test.raw_id_fields/radio_fields/filter_horizontalflipped to ✅; stale "does NOT carry through" rows removed; new section forempty_value_display, customeach_context,list_select_related.t().FieldInputlookup/select/placeholder strings +App.tsx"Page not found." routed throught(); es/fr/pt catalog keys added.LoginPage/CreatePageReact.lazyat the route boundary;?alllist path usescontent-visibilityrow windowing.Decisions / caveats
unsupported_widget(visible note).autocomplete-multiplefor a large M2M with no inlined choices degrades to the existing read-only M2M path.select-daterenders a single<input type=date>(same ISO value as Django's 3 selects, no semantics lost) rather than rebuilding three coupled selects.django_admin_rest_apito the test project's INSTALLED_APPS (matches its design; the [enhancement] Add django.core.checks for startup misconfiguration with actionable hints #667 check expects it).Verification (all green)
ruff check✅,ruff format --check✅,mypy django_admin_react✅ (no issues, 9 files),bandit✅ (0 findings),pytest✅ 75 passed.pnpm typecheck✅,pnpm lint:js✅,pnpm lint:css✅,pnpm test✅ 248 passed (35 files),pnpm build✅ (LoginPage/CreatePage split into separate chunks).pre-commit run --all-files✅ on in-scope files.Closes #664 #665 #666 #667 #668 #669 #670
🤖 Generated with Claude Code